<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Go by Example 中文版: 原子计数器</title>
    <link rel=stylesheet href="site.css">
  </head>
  <script>
      onkeydown = (e) => {
          
          if (e.key == "ArrowLeft") {
              window.location.href = 'rate-limiting';
          }
          
          
          if (e.key == "ArrowRight") {
              window.location.href = 'mutexes';
          }
          
      }
  </script>
  <body>

    <div class="example" id="atomic-counters">
      <h2><a href="./">Go by Example 中文版</a>: 原子计数器</h2>
      
      <table>
        
        <tr>
          <td class="docs">
            <p>Go 中最主要的状态管理机制是依靠通道间的通信来完成的。
我们已经在<a href="worker-pools">工作池</a>的例子中看到过，并且，还有一些其他的方法来管理状态。
这里，我们来看看如何使用 <code>sync/atomic</code> 包在多个协程中进行 _原子计数_。</p>

          </td>
          <td class="code empty leading">
            
          
          </td>
        </tr>
        
        <tr>
          <td class="docs">
            
          </td>
          <td class="code leading">
            <a href="http://play.golang.org/p/yaORzjpC_tZ"><img title="Run code" src="play.png" class="run" /></a><img title="Copy code" src="clipboard.png" class="copy" />
          <div class="highlight"><pre><span class="kn">package</span> <span class="nx">main</span>
</pre></div>

          </td>
        </tr>
        
        <tr>
          <td class="docs">
            
          </td>
          <td class="code leading">
            
          <div class="highlight"><pre><span class="kn">import</span> <span class="p">(</span>
    <span class="s">&quot;fmt&quot;</span>
    <span class="s">&quot;sync&quot;</span>
    <span class="s">&quot;sync/atomic&quot;</span>
<span class="p">)</span>
</pre></div>

          </td>
        </tr>
        
        <tr>
          <td class="docs">
            
          </td>
          <td class="code leading">
            
          <div class="highlight"><pre><span class="kd">func</span> <span class="nx">main</span><span class="p">()</span> <span class="p">{</span>
</pre></div>

          </td>
        </tr>
        
        <tr>
          <td class="docs">
            <p>我们将使用一个无符号整型（永远是正整数）变量来表示这个计数器。</p>

          </td>
          <td class="code leading">
            
          <div class="highlight"><pre>    <span class="kd">var</span> <span class="nx">ops</span> <span class="kt">uint64</span>
</pre></div>

          </td>
        </tr>
        
        <tr>
          <td class="docs">
            <p>WaitGroup 帮助我们等待所有协程完成它们的工作。</p>

          </td>
          <td class="code leading">
            
          <div class="highlight"><pre>    <span class="kd">var</span> <span class="nx">wg</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">WaitGroup</span>
</pre></div>

          </td>
        </tr>
        
        <tr>
          <td class="docs">
            <p>我们会启动 50 个协程，并且每个协程会将计数器递增 1000 次。</p>

          </td>
          <td class="code leading">
            
          <div class="highlight"><pre>    <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p">&lt;</span> <span class="mi">50</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
        <span class="nx">wg</span><span class="p">.</span><span class="nx">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</pre></div>

          </td>
        </tr>
        
        <tr>
          <td class="docs">
            <p>使用 <code>AddUint64</code> 来让计数器自动增加，
使用 <code>&amp;</code> 语法给定 <code>ops</code> 的内存地址。</p>

          </td>
          <td class="code leading">
            
          <div class="highlight"><pre>        <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
            <span class="k">for</span> <span class="nx">c</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">c</span> <span class="p">&lt;</span> <span class="mi">1000</span><span class="p">;</span> <span class="nx">c</span><span class="o">++</span> <span class="p">{</span>
</pre></div>

          </td>
        </tr>
        
        <tr>
          <td class="docs">
            
          </td>
          <td class="code leading">
            
          <div class="highlight"><pre>                <span class="nx">atomic</span><span class="p">.</span><span class="nx">AddUint64</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">ops</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
            <span class="p">}</span>
            <span class="nx">wg</span><span class="p">.</span><span class="nx">Done</span><span class="p">()</span>
        <span class="p">}()</span>
    <span class="p">}</span>
</pre></div>

          </td>
        </tr>
        
        <tr>
          <td class="docs">
            <p>等待，直到所有协程完成。</p>

          </td>
          <td class="code leading">
            
          <div class="highlight"><pre>    <span class="nx">wg</span><span class="p">.</span><span class="nx">Wait</span><span class="p">()</span>
</pre></div>

          </td>
        </tr>
        
        <tr>
          <td class="docs">
            <p>现在可以安全的访问 <code>ops</code>，因为我们知道，此时没有协程写入 <code>ops</code>，
此外，还可以使用 <code>atomic.LoadUint64</code> 之类的函数，在原子更新的同时安全地读取它们。</p>

          </td>
          <td class="code">
            
          <div class="highlight"><pre>    <span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="s">&quot;ops:&quot;</span><span class="p">,</span> <span class="nx">ops</span><span class="p">)</span>
<span class="p">}</span>
</pre></div>

          </td>
        </tr>
        
      </table>
      
      <table>
        
        <tr>
          <td class="docs">
            <p>预计会进行 50,000 次操作。如果我们使用非原子的 <code>ops++</code> 来增加计数器，
由于多个协程会互相干扰，运行时值会改变，可能会导致我们得到一个不同的数字。
此外，运行程序时带上 <code>-race</code> 标志，我们可以获取数据竞争失败的详情。</p>

          </td>
          <td class="code leading">
            
          <div class="highlight"><pre><span class="gp">$</span> go run atomic-counters.go
<span class="go">ops: 50000</span>
</pre></div>

          </td>
        </tr>
        
        <tr>
          <td class="docs">
            <p>接下来，我们看一下管理状态的另一个工具——互斥锁。</p>

          </td>
          <td class="code empty">
            
          
          </td>
        </tr>
        
      </table>
      
      
      <p class="next">
        下一个例子: <a href="mutexes">互斥锁</a>
      </p>
      
      <p class="footer">
        <a href="https://twitter.com/mmcgrana">@mmcgrana</a> 编写 | <a href="https://github.com/gobyexample-cn">gobyexample-cn</a> 翻译 | <a href="https://github.com/gobyexample-cn/gobyexample/issues">反馈</a> | <a href="https://github.com/gobyexample-cn/gobyexample">源码</a> | <a href="https://github.com/mmcgrana/gobyexample#license">license</a>      </p>
      </p>
    </div>
    <script>
      var codeLines = [];
      codeLines.push('');codeLines.push('package main\u000A');codeLines.push('import (\u000A    \"fmt\"\u000A    \"sync\"\u000A    \"sync/atomic\"\u000A)\u000A');codeLines.push('func main() {\u000A');codeLines.push('    var ops uint64\u000A');codeLines.push('    var wg sync.WaitGroup\u000A');codeLines.push('    for i := 0; i \x3C 50; i++ {\u000A        wg.Add(1)\u000A');codeLines.push('        go func() {\u000A            for c := 0; c \x3C 1000; c++ {\u000A');codeLines.push('                atomic.AddUint64(&ops, 1)\u000A            }\u000A            wg.Done()\u000A        }()\u000A    }\u000A');codeLines.push('    wg.Wait()\u000A');codeLines.push('    fmt.Println(\"ops:\", ops)\u000A}\u000A');codeLines.push('');codeLines.push('');
    </script>
    <script src="site.js" async></script>
  </body>
</html>
